package org.mokusakura.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhenzi.sms.ZhenziSmsClient;
import org.mokusakura.domain.*;
import org.mokusakura.domain.dto.ZhenziResponse;
import org.mokusakura.persistence.Util.IDWorker;
import org.mokusakura.persistence.Util.VerifyCodeGenerator;
import org.mokusakura.persistence.mapper.*;
import org.mokusakura.service.UserService;
import org.mokusakura.service.util.SendEmailUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class UserServiceImpl implements UserService {
    private final IDWorker idWorker = new IDWorker(1);
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ItemMapper itemMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private CartMapper cartMapper;
    @Autowired
    private LineItemMapper lineItemMapper;
    @Autowired
    private VerificationMapper verificationMapper;
    @Autowired
    private SendEmailUtils sendEmailUtils;
    @Autowired
    private PhoneVerifyCodeMapper phoneVerifyCodeMapper;
    @Autowired
    private ZhenziSmsClient client;

    public User login(String email, String password) {
        return userMapper.selectUserByEmailAndPassword(email, password);
    }

    private String getUserPassword(User user) {
        User correctUser = userMapper.selectUserByEmail(user.getEmail());
        if (correctUser != null) {
            return correctUser.getPassword();
        }
        return null;
    }


    public boolean isRightPassword(User user, String password) {
        return password.equals(this.getUserPassword(user));
    }

    @Transactional
    public User register(User user) throws Exception {
        if (userMapper.selectUserByEmail(user.getEmail()) != null) {
            return null;
        }
        /*
         * I think I shall make all fields here
         * What a big job!
         */

        //Does a user must have a cart?
        user.initUser();
        if (userMapper.insertUserIntoLoginUser(user).equals(1)
                && userMapper.insertUserIntoAccount(user).equals(1)
                && userMapper.insertUserIntoUserFile(user).equals(1))
            return user;
        throw new Exception("Unknown Error!");
    }

    @Override
    @Transactional
    public Integer sendVerificationEmail(String email) {
        User user = userMapper.selectUserByEmail(email);
        if (user == null) {
            return 0;
        }
        String randomString = VerifyCodeGenerator.randomString(6);
        VerifyCode verifyCode = new VerifyCode()
                .setEmail(email)
                .setUserId(user.getId())
                .setVerifyCode(randomString)
                .setExpiryTime(new Timestamp(System.currentTimeMillis() + 1800000))
                .setActive(true);

        Integer res = verificationMapper.insert(verifyCode);
        if (res.equals(1)) {
            if (sendEmailUtils.sendSimpleEmail(email, "登录验证码", "您的验证码是" + randomString)) {
                return 1;
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }

    @Transactional
    public User update(User user) throws Exception {

        if (userMapper.updateUserInLoginUser(user).equals(1)
                && userMapper.updateUserInAccount(user).equals(1)
                && userMapper.updateUserInUserFile(user).equals(1)) {
            user = userMapper.selectUserByUserId(user.getId());
            return user;
        } else {
            throw new Exception("Unknown Error!");
        }
//        String newPassword = user.getPassword();
//        String correctPassword = this.getUserPassword(user);
//        if (newPassword == null || newPassword.isEmpty()) {
//            user.setPassword(correctPassword);
//            if (userMapper.updateUserInLoginUser(user).equals(1) &&
//            userMapper.updateUserInUserFile(user).equals(1) &&
//            userMapper.updateUserInAccount(user).equals(1)) {
//                return user;
//            }
//        } else if (correctPassword != null && correctPassword.equals(currentPassword) &&
//                RegularExpressionVerifyUtil.isLegalPassword(user.getPassword())) {
//            user = userDao.updateUserInfo(user);
//        } else {
//            user = null;
//        }
//        //else if(!RegularExpressionVerifyUtil.isLegalPassword(user.getPassword()))
//        //return null;
//
//        if (user != null) {
//            user = userDao.getUserByEmail(user.getEmail());
//            user.setCart(cartDAO.getUserCart(user));
//            user.setPassword(null);
//        }
//        return user;
    }

    @Transactional
    public Integer addItemToCart(User user, String itemId) throws Exception {

        Item item = itemMapper.getItem(itemId);
        //Item item = user.getCart().getItemCartItemMap().get(itemId).getItem();
        Cart cart = user.getCart();
        if (cart == null) {
            user.setCart(new Cart());
            cart = user.getCart();
        }
        for (CartItem cartItem : cart.getItemCartItemMap().values()) {
            if (cartItem.getItemId().equals(itemId)) {
                return 0;
            }
        }
        CartItem cartItem = new CartItem();
        cartItem.setItem(item)
                .setUserId(user.getId())
                .setQuantity(1)
                .setItemId(itemId)
                .setInStock(item.getQuantity() >= 0);
        int res = 0;
        try {
            res = cartMapper.insertCartItem(cartItem);
        } catch (Exception ignored) {
            //通常是由重复添加引起的
        }
        return res;
    }

    @Transactional
    public Integer updateCartItemQuantity(User user, String itemId, Integer quantity) throws Exception {
        int res = cartMapper.updateQuantity(user.getId(), itemId, quantity);
        if (res == 1) {
            Cart cart = user.getCart();
            CartItem cartItem = cart.getItemCartItemMap().get(itemId);
            cartItem.setQuantity(quantity);
            return res;
        }
        throw new Exception("Unknown Wrong!");

    }

    @Transactional
    public Integer deleteItem(User user, String itemId) throws Exception {
        int res = cartMapper.deleteCartItem(user.getId(), itemId);
        if (res == 1) {
            Cart cart = user.getCart();
            cart.getItemCartItemMap().remove(itemId);
            return res;
            //cart.calculateTotal();
        }
        throw new Exception("Unknown Wrong!");
    }

    @Override
    public Cart getUserCart(User user) {
        List<CartItem> cartItemListByUserId = cartMapper.getCartItemListByUserId(user.getId());
        Cart cart = new Cart();
        cartItemListByUserId.forEach(cartItem -> cart.addCartItem(cartItem.getItemId(), cartItem));
        return cart;
    }

    public User getUserByEmail(String email) {
        return userMapper.selectUserByEmail(email);
    }

    @Override
    @Transactional
    public User loginWithVerifyCode(String email, String verifyCode) {
        VerifyCode verifyCode1 = verificationMapper.selectCorrectAndEffectiveVerifyCodeByEmail(email, new Timestamp(
                System.currentTimeMillis()), verifyCode);
        if (verifyCode1 != null) {
            verifyCode1.setActive(false);
            verificationMapper.updateById(verifyCode1);
            return userMapper.selectUserByEmail(email);
        } else {
            return null;
        }
    }

    public List<Order> getOrderList(User user) {
        return orderMapper.getOrderListByUserId(user.getId());
    }

    @Override
    @Transactional
    public Boolean sendText(String phoneNumber) {
        User user = userMapper.selectUserByPhoneNumber(phoneNumber);
        if (user != null) {
            String verifyCode = VerifyCodeGenerator.randomString(6);
            PhoneVerifyCode phoneVerifyCode = new PhoneVerifyCode();
            phoneVerifyCode.setVerifyCode(verifyCode)
                    .setUserId(user.getId())
                    .setActive(true)
                    .setExpiryTime(new Timestamp(System.currentTimeMillis() + 300000));
            phoneVerifyCodeMapper.insert(phoneVerifyCode);
            Map<String, Object> params = new HashMap<>(3);
            params.put("number", phoneNumber);
            params.put("templateId", "4485");
            String[] templateParams = new String[2];
            templateParams[0] = verifyCode;
            templateParams[1] = "5";
            params.put("templateParams", templateParams);
            try {
                String res = client.send(params);
                ObjectMapper mapper = new ObjectMapper();
                ZhenziResponse zhenziResponse = mapper.readValue(res, ZhenziResponse.class);
                return zhenziResponse.getCode() == 0;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    @Override
    @Transactional
    public User loginWithPhoneVerifyCode(String phoneNumber, String phoneVerifyCode) {
        PhoneVerifyCode phoneVerifyCode1 = phoneVerifyCodeMapper.selectCorrectAndEffectiveVerifyCodeByPhone(phoneNumber,
                                                                                                            phoneVerifyCode);
        if (phoneVerifyCode1 != null) {
            phoneVerifyCode1.setActive(false);
            phoneVerifyCodeMapper.updateById(phoneVerifyCode1);
            return userMapper.selectUserByUserId(phoneVerifyCode1.getUserId());
        } else {
            return null;
        }
    }

    @Deprecated
    public Order getOrderByOrderId(Long orderId) {
        return orderMapper.getOrderByOrderId(orderId);
    }


    @Deprecated
    //I don't fell right here
    //Does these three match MVC?
    //By the way the fields of Order are too much!!!
    public Boolean purchaseStepOne(HttpServletRequest req) {
        long orderID = idWorker.nextId();
        User user = (User) req.getSession().getAttribute("loginUser");
        String cardType = req.getParameter("order.cardType");
        String cardNumber = req.getParameter("order.creditCard");
        String expiryDate = req.getParameter("order.expiryDate");
        String billToFirstName = req.getParameter("order.billToFirstName");
        String billToLastName = req.getParameter("order.billToLastName");
        String billAddress1 = req.getParameter("order.billAddress1");
        String billCity = req.getParameter("order.billCity");
        String billState = req.getParameter("order.billState");
        String billZip = req.getParameter("order.billZip");
        String billCountry = req.getParameter("order.billCountry");
        Order order = new Order();
        order.setCourier("SF");
        order.setEmail(user.getEmail());
        order.setOrderId(orderID);
        order.setCardType(cardType);
        order.setCreditCard(cardNumber);
        order.setExpiryDate(expiryDate);
        order.setBillToFirstName(billToFirstName);
        order.setBillToLastName(billToLastName);
        order.setBillAddress(billAddress1);
        order.setBillCity(billCity);
        order.setBillState(billState);
        order.setBillZip(billZip);
        order.setBillCountry(billCountry);
        order.setLineItems(createLineItemListFromCart(user.getCart(), orderID));
        //System.out.println(order.getTotalPrice());
        req.getSession().setAttribute("newOrder", order);
        return Boolean.parseBoolean(req.getParameter("shippingAddressRequired"));
    }

    @Deprecated
    public Boolean purchaseStepTwo(HttpServletRequest req, boolean pattern) {
        Order order = (Order) req.getSession().getAttribute("newOrder");
        if (order == null) {
            return false;
        }
        if (pattern) {
            order.setShipToFirstName(req.getParameter("firstName"));
            order.setShipToLastName(req.getParameter("lastName"));
            order.setShipAddress(req.getParameter("address"));
            order.setShipCity(req.getParameter("city"));
            order.setShipState(req.getParameter("state"));
            order.setShipZip(req.getParameter("zip"));
            order.setShipCountry(req.getParameter("country"));
        } else {
            order.setShipToFirstName(order.getBillToFirstName());
            order.setShipToLastName(order.getBillToLastName());
            order.setShipAddress(order.getBillAddress());
            order.setShipCity(order.getBillCity());
            order.setShipState(order.getBillState());
            order.setShipZip(order.getBillZip());
            order.setShipCountry(order.getBillCountry());
        }
        return true;
    }

    @Deprecated
    public Integer purchaseStepThree(HttpServletRequest req) {
        Order order = (Order) req.getSession().getAttribute("newOrder");

        //quantity check
        for (LineItem lineItem : order.getLineItems()) {
            String itemID = lineItem.getItemId();
            int quantity = lineItem.getQuantity();
            if (quantity > itemMapper.getInventoryQuantity(itemID)) {
                return -1;
            }
        }

        User user = (User) req.getSession().getAttribute("loginUser");
        Cart cart = user.getCart();
        long orderID = order.getOrderId();
        order.setOrderDate(new Timestamp(System.currentTimeMillis()));
        orderMapper.insertOrder(order);

        for (LineItem lineItem : order.getLineItems()) {
            String itemID = lineItem.getItemId();
            int quantity = lineItem.getQuantity();
            int i = lineItemMapper.insertLineItem(lineItem);
            itemMapper.updateInventoryQuantity(itemID, quantity);
            if (i != 1) {
                return 0;
            }
        }
        List<Order> orderList = orderMapper.getOrderListByUserId(user.getId());
        req.getSession().setAttribute("orderList", orderList);
        cart.getItemCartItemMap().clear();
        cartMapper.deleteCartItem(user.getId(), null);
        return 1;
    }

    private LineItem createLineItemFromCartItem(CartItem cartItem, long orderID) {
        LineItem lineItem = new LineItem();
        lineItem.setItemId(cartItem.getItem().getItemId());
        lineItem.setQuantity(cartItem.getQuantity());
        lineItem.setOrderId(orderID);
        return lineItem;
    }

    private List<LineItem> createLineItemListFromCart(Cart cart, long orderID) {
        List<LineItem> lineItemList = new ArrayList<>();
        for (CartItem cartItem : cart.getItemCartItemMap().values()) {
            LineItem lineItem = createLineItemFromCartItem(cartItem, orderID);
            lineItemList.add(lineItem);
        }
        return lineItemList;
    }


}
